package com.ruoyi.flow.common.tools.mediaconverter.converter.ffmpeg;

import com.ruoyi.flow.common.tools.mediaconverter.converter.command.CommandInvoker;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeoutException;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

/**
 * FFMPEG执行程序
 */
public class FfmpegInvoker {
    private static final Logger LOGGER = LoggerFactory.getLogger(FfmpegInvoker.class);
    private static final String cmdOnLinux = "ffmpeg";
    private static final String cmdOnWindows = "ffmpeg.exe";
    /**
     * ffmpeg所在目录的路径
     */
    private String ffmpegDirPath;
    /**
     * 参数
     */
    private List<String> args = new ArrayList<String>();

    private List<String> stdInputString;

    private String command;

    private long timeout = 30000;

    private static final boolean IS_LINUX = SystemUtils.IS_OS_LINUX;

    public List<String> getStdInputString() {
        return this.stdInputString;
    }

    private FfmpegInvoker(String ffmpegDirPath) {
        checkNotNull(ffmpegDirPath);
        this.ffmpegDirPath = ffmpegDirPath;
        if (IS_LINUX) {
            this.command = cmdOnLinux;
        } else {
            this.command = ffmpegDirPath + cmdOnWindows;
        }
        this.timeout = timeout;
    }

    public static FfmpegInvoker on(FfmpegLocator locator) {
        if (locator == null) {
            locator = FfmpegLocator.getLocator();
        }
        return on(locator.getFfmpegDir());
    }

    public static FfmpegInvoker on(String ffmpegDirPath) {
        checkNotNull(ffmpegDirPath);
        File ffmpegDir = new File(ffmpegDirPath);
        checkState(ffmpegDir.exists(), "ffmpeg dir {} not exist.", ffmpegDirPath);
        ffmpegDirPath = ffmpegDir.getAbsolutePath();
        if (!StringUtils.endsWith(ffmpegDirPath, File.separator)) {
            ffmpegDirPath += File.separator;
        }
        return new FfmpegInvoker(ffmpegDirPath);
    }

    /**
     * 设置运行超时时间，如果设置为-1，则会阻塞至运行结束，可能会造成死锁
     *
     * @param timeout
     * @return
     */
    public FfmpegInvoker timeout(long timeout) {
        this.timeout = timeout;
        return this;
    }

    /**
     * 添加脚本参数
     *
     * @param arg
     * @return
     */
    public FfmpegInvoker addArg(String arg) {
        checkNotNull(arg);
        if (!args.contains(arg)) {
            args.add(arg);
        }
        return this;
    }

    /**
     * 添加多个脚本参数
     *
     * @param pargs
     * @return
     */
    public FfmpegInvoker addArgs(String... pargs) {
        if (pargs.length > 0) {
            for (String arg : pargs) {
                if (!args.contains(arg)) {
                    args.add(arg);
                }
            }
        }
        return this;
    }

    /**
     * 执行脚本
     *
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    public void invoke() {
        CommandInvoker invoker = new CommandInvoker(this.command, this.ffmpegDirPath, this.args);
        try {
            invoker.invoke(this.timeout);
            this.stdInputString = invoker.getStdInputString();
        } catch (IOException e) {
            LOGGER.error("执行ffmpeg命令异常，请确认是否安装ffmpeg在目录{}", this.toString(), e);
        } catch (InterruptedException e) {
            LOGGER.error("执行ffmpeg命令被中断", e);
        } catch (TimeoutException e) {
            LOGGER.error("执行ffmpeg超时", e);
        } finally {
            invoker.destroy();
        }
    }

    @Override
    public String toString() {
        return "ffmpegDirPath=" + ffmpegDirPath + ",command=" + command;
    }
}
